#define _DEFAULT_SOURCE
#include <errno.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>

#include "process.h"
#include "frame.h"

#define TRACE                                       \
    do {                                            \
        printf("%s:%d\n", __FUNCTION__, __LINE__);  \
    } while(0)


enum { INPUT = 0, OUTPUT = 1, PIPE = 2 };

int pipefds[PIPE * 3];
int *first = &pipefds[0],
    *second = &pipefds[2],
    *third = &pipefds[4];

void close_pipes_except(int fd1, int fd2);
void grandchild(void);
void child(void);
Frame parent(Frame *f);

int main(void)
{

    int retval = 0;
    char *line = NULL;
    size_t size;
    while (1)
    {
        if (getline(&line, &size, stdin) == -1)
            break;

        Frame frame = frame_tokenize_cstr(line);
        if (frame.id == -1) continue;

        Frame received = parent(&frame);
        print_frame(&received);
    }

    if (errno)
    {
        fprintf(stderr, "ERROR: could not get input from user");
        retval = 1;
    }

    free(line);
    return retval;
}

void close_pipes_except(int fd1, int fd2)
{
    for (int i = 0; i < 6; ++i)
        if (pipefds[i] != fd1 && pipefds[i] != fd2)
            close(pipefds[i]);
}

void grandchild(void)
{
    close_pipes_except(second[INPUT], third[OUTPUT]);

    Frame frame = {0};
    read_frame_from_pipe(&frame, second[INPUT]);

    frame.value *= 3;

    write_frame_to_pipe(&frame, third[OUTPUT]);
}

void child(void)
{
    handle_fork(grandchild);
    close_pipes_except(first[INPUT], second[OUTPUT]);

    Frame frame = {0};

    read_frame_from_pipe(&frame, first[INPUT]);

    frame.value += 2;

    write_frame_to_pipe(&frame, second[OUTPUT]);
    wait_for_child();
}

Frame parent(Frame *frame)
{
    pipe(first);
    pipe(second);
    pipe(third);
    handle_fork(child);

    close_pipes_except(first[OUTPUT], third[INPUT]);
    write_frame_to_pipe(frame, first[OUTPUT]);
    wait_for_child();
    Frame rec = (Frame) {0};
    read_frame_from_pipe(&rec, third[INPUT]);
    return rec;
}
